package main

import (
	"encoding/json"
	"fmt"
	"net"
	"unsafe"
)

// 传输的数据结构
type ChatMsg struct {
	From, To, Msg string //从哪里来，到哪里去，内容
}

// 与客户端通信的结构
type ClientMsg struct {
	To      string  `json:"to"`       //去哪
	Msg     string  `json:"msg"`     //内容
	Datalen uintptr `json:"datalen"` //数据长度
}

// 定义消息中心用的channel
var chan_msgcenter chan ChatMsg

// 定义用户名到ip的map
var mapName2CliAddr map[string]string

// 定义ip到conn的map
var mapCliaddr2Clients map[string]net.Conn

// 定义IP到名称的map
var mapCliaddr2Name map[string]string

// 主函数
func main() {
	//初始化
	mapCliaddr2Clients = make(map[string]net.Conn)
	mapName2CliAddr = make(map[string]string)
	mapCliaddr2Name = make(map[string]string)
	chan_msgcenter = make(chan ChatMsg)
	//监听
	lsner, err := net.Listen("tcp", "localhost:8888")
	if err != nil {
		panic("listening failed!")
	}
	defer lsner.Close()
	//开一个routine起信息中心
	go Msg_center()
	//循环接收消息
	fmt.Println("begin chatroom at server:", lsner.Addr().String())
	for {
		conn, err := lsner.Accept()
		if err != nil {
			fmt.Println("ACCEPT FAILED!")
			break
		}
		fmt.Println("new conn:", conn.RemoteAddr().String())
		//开一个routine处理消息
		go Handle_conn(conn)
	}
}

// 处理断开连接
func Logout(conn net.Conn, from string) {
	defer conn.Close()
	//在连接map里删掉
	delete(mapCliaddr2Clients, from)
	name := mapCliaddr2Name[from]
	delete(mapName2CliAddr, name)
	delete(mapCliaddr2Name, from)
	//告诉消息中心，发给大家退出登录的消息
	msg := ChatMsg{name, "all", from + "->logout"}
	chan_msgcenter <- msg
}
func Msg_center() {
	for {
		//等消息传过来，有了就发
		msg := <-chan_msgcenter
		fmt.Println("center:", msg)
		go send_msg(msg)
	}
}

// 处理新连接
func Handle_conn(conn net.Conn) {
	//获取ip
	from := conn.RemoteAddr().String()
	//在map里添加键值对 ip->连接
	mapCliaddr2Clients[from] = conn
	//告诉所有人登陆了
	msg := ChatMsg{"server", "all", from + " login"}
	fmt.Println(msg)
	chan_msgcenter <- msg
	defer Logout(conn, from)
	//读取信息
	buf := make([]byte, 256)
	for {
		n, err := conn.Read(buf)
		if err != nil {
			fmt.Println("reading failed!")
			break
		}
		if n > 0 {
			//将信息jsondecode后放进climsg结构体
			var climsg ClientMsg
			err := json.Unmarshal(buf[:n], &climsg)
			if err != nil {
				fmt.Println("unmarshaling failed!")
				continue
			}
			//检验格式
			if climsg.Datalen != unsafe.Sizeof(climsg) {
				fmt.Println("unvalid format!", climsg)
				continue
			}
			//预设发送对象为all
			name, ok := mapCliaddr2Name[from]
			if ok {
				from = name
			}
			chatmsg := ChatMsg{from, "all", climsg.Msg}
			//判断发到哪里，可以通过set来设置用户名
			switch climsg.To {
			case "all":
			case "set":
				mapName2CliAddr[climsg.Msg] = from
				chatmsg.Msg = from + " set name=" + climsg.Msg + " success"
				chatmsg.From = "server"
				mapCliaddr2Name[from] = climsg.Msg
			default:
				chatmsg.To = climsg.To
			}
			//传给channel
			chan_msgcenter <- chatmsg
		}
	}

}
func send_msg(msg ChatMsg) {
	//先jsonencode一下
	data, err := json.Marshal(msg)
	if err != nil {
		fmt.Println("marshaling failed!")
		return
	}
	//判断发给谁
	if msg.To == "all" {
		for _, v := range mapCliaddr2Clients {
			//不给自己发
			if msg.From != v.RemoteAddr().String() {
				v.Write(data)
			}
		}
	} else {
		//私聊，判断用户存在和是否在线
		from, ok := mapName2CliAddr[msg.To]
		if !ok {
			fmt.Println("USER not EXISTS!", msg.To)
			return
		}
		conn, ok := mapCliaddr2Clients[from]
		if !ok {
			fmt.Println("cli not exists!", from, msg.To)
			return
		}
		conn.Write(data)
	}
}
